Skip to content

feat(services): add storage, cache, and DB services#983

Open
karlitschek wants to merge 1 commit into
masterfrom
split/06-services-storage-db
Open

feat(services): add storage, cache, and DB services#983
karlitschek wants to merge 1 commit into
masterfrom
split/06-services-storage-db

Conversation

@karlitschek
Copy link
Copy Markdown
Member

Adds five read-only service classes used by the upcoming admin dashboard cards. None are wired into existing controllers yet, so this change is a pure addition with no behavior change.

  • DbHealth - largest tables on MySQL / PostgreSQL
  • CachingInfo - OPcache and APCu hit rates plus Redis config
  • ExternalStoragesInfo - mounts configured via files_external
  • DiskGrowth - rolling history snapshots with a
    days-until-full prediction
  • TopUsersByQuota - top users ranked by home::*/files size

Adds five read-only service classes used by the upcoming admin
dashboard cards. None are wired into existing controllers yet, so
this change is a pure addition with no behavior change.

* DbHealth             - largest tables on MySQL / PostgreSQL
* CachingInfo          - OPcache and APCu hit rates plus Redis config
* ExternalStoragesInfo - mounts configured via files_external
* DiskGrowth           - rolling history snapshots with a
                         days-until-full prediction
* TopUsersByQuota      - top users ranked by home::*/files size

Signed-off-by: Frank Karlitschek <frank@nextcloud.com>
Comment thread lib/DbHealth.php
}
$sql = 'SELECT table_name AS name, table_rows AS rows, '
. '(data_length + index_length) AS size_bytes '
. 'FROM information_schema.TABLES WHERE table_schema = ? '
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This definitely needs error handler because by default Nextcloud drops admin privilege by creating a dedicated user during setup.

Checking options …

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should work when mariadb allows seeing the objects the user has access to. The only remaining issue is that the information_schema access is notorious for rebuilding database metadata.

Accepting the risk for now to get this merged.

Comment thread lib/DbHealth.php
$stmt->bindValue(1, $dbName);
$result = $stmt->executeQuery();
$out = [];
while (($row = $result->fetch()) !== false) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer using fetchAssociative/fetchNumeric/fetchOne or iterateAssociate/iterateNumeric instead.

Comment thread lib/DbHealth.php
* @return list<array{name: string, rows: int, sizeBytes: int}>
*/
private function pgTables(int $limit = 8): array {
$sql = 'SELECT relname AS name, n_live_tup AS rows, '
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Neither pg_class or pg_namespace have a column "n_live_tup".

I'm going to check ...

Copy link
Copy Markdown
Collaborator

@kesselb kesselb May 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SELECT
    relname as name,
    n_live_tup as rows,
    pg_total_relation_size(relid) AS size_bytes
FROM pg_stat_user_tables
ORDER BY size_bytes DESC
LIMIT 8;
Image

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pg_stat_user_tables

Should list all tables the current user has access too

Comment thread lib/DiskGrowth.php
$shouldSample = $history === [] || ($now - $history[count($history) - 1]['ts']) >= self::SAMPLE_INTERVAL;

if ($shouldSample) {
$dataDir = (string)$this->config->getSystemValue('datadirectory', '');
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the sampling should go to

public function updateStorageCounts(): void {
(which is called by background job every 3 hours)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants